﻿//Copyright (c) 2008 Michael J. Ryan & Apollo Group, Inc.

//Permission is hereby granted, free of charge, to any person
//obtaining a copy of this software and associated documentation
//files (the "Software"), to deal in the Software without
//restriction, including without limitation the rights to use,
//copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the
//Software is furnished to do so, subject to the following
//conditions:

//The above copyright notice and this permission notice shall be
//included in all copies or substantial portions of the Software.

//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Apollo.Common.Configuration
{
	public class ConfigurationFile
	{
		private object _hnd;
		private string _name;
		private DateTime _pulse;
		private DateTime _loaded;
		private FileInfo _file;
		private string _text;
		private ConfigurationSet _config;
		private Func<string, FileInfo> _getFileInfo;
	
		public event EventHandler<EventArgs> Deleted;
	
		internal ConfigurationFile(string configName, Func<string, FileInfo> fileInfoLookup) {
			_hnd = new object();
			_name = configName;
			_pulse = DateTime.Now;
			_loaded = DateTime.MinValue;
			_file = null;
			_text = null;
			_config = null;
			_getFileInfo = fileInfoLookup;
		}
		
		public string Name
		{
			get {
				return _name;
			}
		}
		
		public bool IsStale
		{
			get {
				DateTime p = _pulse; //local value
				return p > DateTime.Now.AddHours(-1); //is it more than an hour old.
			}
		}
		
		public FileInfo FileInfo
		{
			get {
				LoadFileInfo();
				return _file;
			}
		}
	
		public string Text
		{
			get {
				lock(_hnd) {
					LoadText();
					return _text;
				}
			}
		}
		
		public ConfigurationSet ConfigurationSet
		{
			get {
				lock(_hnd) {
					LoadConfig();
					return _config ?? ConfigurationSet.Empty;
				}
			}
		}
		
		private void LoadFileInfo()
		{
			lock(_hnd) {
				try
				{
					if (_file != null)
						_file.Refresh();
				
					//if the fileinfo is stale, refresh/reload it
					if (_file == null || !_file.Exists) {
						//haven't loaded settings, or settings file moved/changed
						_text = null;	//clear loaded text
						_config = null;	//clear loaded config
						_file = _getFileInfo(_name);
					}
					
					//fileinfo is still missing
					if (_file == null || !_file.Exists) {
						//no new config file to load from
						_pulse = DateTime.MinValue;
						LogTool.Logger.Warn("Apollo.Common.Configuration..ConfigurationFile.LoadFileInfo: Configuration file missing, raising Deleted event.");
						if (Deleted != null)
							Deleted(this, EventArgs.Empty); //bad file, or deleted, without replacement
					} else
						_pulse = DateTime.Now;
				}
				catch (Exception ex)
				{
					LogTool.Logger.ErrorException("Apollo.Common.Configuration..ConfigurationFile.LoadFileInfo: Exception while loading FileInfo.", ex);
					throw;
				}
			}
		}
		
		private void LoadText()
		{
			lock(_hnd) {
				try {
					LoadFileInfo();
					
					if (_file == null)
						return;

					if (_text == null || _file.LastWriteTime > _loaded) {
						//need to load the text from the config.
						//read the file from disk.
						using (StreamReader sr = _file.OpenText()) {
							_text = sr.ReadToEnd();
							sr.Close();
							_loaded = DateTime.Now;
						}
					}
				}
				catch (Exception ex)
				{
					_file = null;
					_text = null;
					_config = null;
					LogTool.Logger.ErrorException("Apollo.Common.Configuration..ConfigurationFile.LoadText: An exception occurred while trying to read the configuration file.", ex);
					throw;
				}
			}
		}
		
		private void LoadConfig()
		{
			lock(_hnd) {
				LoadText();
				if (_text != null && _config == null) {
					try
					{
						LoadText();
						_config = ConfigurationSet.LoadConfig(_text);
					}
					catch (Exception ex)
					{
						_config = null;
						LogTool.Logger.ErrorException("Apollo.Common.Configuration..ConfigurationFile.LoadConfig: Exception while loading ConfigurationSet.", ex);
						throw;
					}
				}
			}
		}
		
	}
}
